/*
 * MIT License
 *
 * Copyright (c) 2023 北京凯特伟业科技有限公司
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.je.document.service.file.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.google.common.base.Charsets;
import com.google.common.base.Strings;
import com.je.common.base.DynaBean;
import com.je.common.base.document.ContentTypeSuffixEnum;
import com.je.common.base.document.FileSaveDTO;
import com.je.common.base.document.UploadFileDTO;
import com.je.common.base.service.MetaService;
import com.je.common.base.util.ZipUtilExt;
import com.je.document.constants.DigestEnum;
import com.je.document.constants.SaveTypeEnum;
import com.je.document.entity.DocumentBucketDO;
import com.je.document.entity.DocumentFileDO;
import com.je.document.exception.DocumentException;
import com.je.document.exception.DocumentExceptionEnum;
import com.je.document.model.Bucket;
import com.je.document.reflection.BeanMappingHelper;
import com.je.document.service.FileAutoGenerator;
import com.je.document.service.FileOperateFactory;
import com.je.document.service.bucket.BucketService;
import com.je.document.service.file.FileService;
import com.je.document.service.impl.FileOperateService;
import com.je.document.util.ThumbnailsUtil;
import com.je.ibatis.extension.conditions.ConditionsWrapper;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 文件通用业务处理实现
 *
 * @author wangmm@ketr.com.cn
 * @date 2019/8/27
 */
@Service
public class FileServiceImpl implements FileService {

    private final Logger log = LoggerFactory.getLogger(getClass());

    @Autowired
    private MetaService metaService;
    @Autowired(required = false)
    private FileAutoGenerator fileAutoGenerator;
    @Autowired
    private BucketService bucketService;

    @Override
    public FileSaveDTO saveFile(String fileName, String contentType, byte[] byteArray, Bucket bucket, String dir) {
        ByteArrayInputStream temp = null;
        try {
            //获取bucket对应的文件操作工具实例
            FileOperateService fileOperate = FileOperateFactory.getInstance(bucket);

            //拼接文件路径
            StringBuilder filePath = new StringBuilder();
            String fullName = "";
            if (bucket.getSaveType().equals(SaveTypeEnum.mongodb.getCode())) {
                fullName = fileName;
                filePath.append(String.format("%s/%s", bucket.getBasePath(), fileName));
            } else {
                //拼接目录  指定目录 > 自定义目录规则 > 默认目录规则
                if (StringUtils.isNotBlank(dir)) {
                    filePath.append(dir);
                } else {
                    filePath.append(fileAutoGenerator.pathGenerator(fileName, contentType, byteArray, bucket));
                }
                //生成文件名 自定义名称规则 > 默认UUID命名规则
                fullName = fileAutoGenerator.fileNameGenerator(fileName, contentType, byteArray, bucket);
                filePath.append("/").append(fullName);
            }

            //获取文件名及后缀名
            String[] splits = fullName.split("\\.");
            String name = splits[0];
            String suffix = splits.length == 1 ? "" : splits[splits.length - 1];

            //上传文件
            temp = IoUtil.toStream(byteArray);
            UploadFileDTO dto = fileOperate.uploadFile(bucket.getBucket(), filePath.toString(), temp);
            //基础路径
            String basePath = fileOperate.getBasePath();
            //设置信息
            FileSaveDTO fileSaveDTO = new FileSaveDTO();
            //mongodb 上传成功回调id
            if (bucket.getSaveType().equals(SaveTypeEnum.mongodb.getCode())) {
                name = dto.getFilePath().split("/")[1];
            }
            fileSaveDTO.setName(name);
            fileSaveDTO.setSuffix(suffix);
            fileSaveDTO.setFullName(fullName);

            fileSaveDTO.setFilePath(filePath.toString());
            fileSaveDTO.setFullUrl(dto.getFullUrl());

            fileSaveDTO.setBucket(bucket.getBucket());
            fileSaveDTO.setSaveType(bucket.getSaveType());
            fileSaveDTO.setPermission(bucket.getPermission());

            //上传缩略图
            if (fileAutoGenerator.thumbnailGenerator()) {
                InputStream thumbnail = thumbnail(bucket.getBucket(), byteArray, contentType, suffix, basePath);
                if (thumbnail != null) {
//                    log.info("file-path   "+bucket.getBasePath()+filePath);
//                    log.info("thumbnail-path   "+filePath.append(".thumbnail"));
                    UploadFileDTO thumbnailDto = fileOperate.uploadFile(bucket.getBucket(), filePath.append(".thumbnail").toString(), thumbnail);
                    fileSaveDTO.setThumbnail(filePath.toString());
                }
            }
            return fileSaveDTO;
        } catch (DocumentException e) {
            throw e;
        } catch (RuntimeException e) {
            throw new DocumentException(DocumentExceptionEnum.DOCUMENT_MANAGER_FILE_SAVE_FAIL, e);
        } finally {
            IoUtil.close(temp);
        }
    }

    @Override
    public FileSaveDTO saveFile(String fileName, String contentType, InputStream inputStream, String bucket) {

        //读取
        byte[] bytes = IoUtil.readBytes(inputStream);
        //关闭流
        IoUtil.close(inputStream);
        return saveFile(fileName, contentType, bytes, bucket);
    }

    @Override
    public String messageDigest(byte[] byteArray, DigestEnum digestEnum) {

        if (digestEnum == DigestEnum.MD5) {
            return DigestUtil.md5Hex(byteArray);
        } else if (digestEnum == DigestEnum.SHA1) {
            return DigestUtil.sha1Hex(byteArray);
        } else if (digestEnum == DigestEnum.SHA256) {
            return DigestUtil.sha256Hex(byteArray);
        }
        return null;
    }

    @Override
    public InputStream thumbnail(String subfolder, byte[] byteArray, String contentType, String suffix, String basePath) {
        if (StringUtils.isBlank(contentType)) {
            return null;
        }
        try {
            boolean needFormat = StringUtils.isBlank(contentType) || ContentTypeSuffixEnum.stream.getContentType().equals(contentType);
            if (needFormat && StringUtils.isNotBlank(suffix)) {
                contentType = ContentTypeSuffixEnum.contentType(suffix);
            }
            //图片缩略图
            if (contentType.contains("image/")) {
                byte[] bytes = ThumbnailsUtil.imageThumbnail(subfolder, byteArray, basePath);
                return IoUtil.toStream(bytes);
            }
        } catch (IOException e) {
            log.warn("{} 类型缩略图生成失败！", contentType);
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public InputStream readFile(String filePath, String bucket) {
        //获取bucket信息
        Bucket bucketBO = findBucket(bucket);
        //获取bucket对应的文件操作工具实例
        FileOperateService fileOperate = FileOperateFactory.getInstance(bucketBO);
        //校验文件是否存在
//        if (!existFile(filePath, bucketBO)) {
//            log.warn("File not find in bucket, bucket:[{}], filePath:[{}]", bucket, filePath);
//            throw new DocumentException(DocumentExceptionEnum.DOCUMENT_MANAGER_FILE_NOT_EXIST);
//        }
        return fileOperate.readFile(bucket, filePath);
    }

    @Override
    public InputStream buildZip(List<String> relNameList, List<String> filePathList, List<String> bucketList) {
        //校验参数是否一致
        if (relNameList.size() != filePathList.size() || relNameList.size() != bucketList.size()) {
            throw new IllegalArgumentException("buildZip params length is not equals to ins length !");
        }
        //文件路径
        List<String> paths = new ArrayList<>();
        //文件流
        List<InputStream> ins = new ArrayList<>();
        for (int i = 0; i < relNameList.size(); i++) {
            //文件路径
            paths.add(System.currentTimeMillis() + "_" + relNameList.get(i));
            //文件流
            ins.add(readFile(filePathList.get(i), bucketList.get(i)));
        }
        try {
            //打包获取输入流
            InputStream inputStream = ZipUtilExt.zip(paths, ins, Charsets.UTF_8);
            return inputStream;
        } catch (Exception e) {
            e.printStackTrace();
            throw new DocumentException(DocumentExceptionEnum.DOCUMENT_MANAGER_ZIP_BUILd_ERROR);
        }
    }

    @Override
    public Bucket findBucket(String bucket) {
        //查找bucket实体
        DocumentBucketDO bucketDO = bucketService.findBucket(bucket);
        if (bucketDO == null) {
            throw new DocumentException(DocumentExceptionEnum.DOCUMENT_MANAGER_BUCKET_NOT_FIND);
        }

        //bucket业务对象
        Bucket bucketBO = new Bucket();
        BeanUtil.copyProperties(bucketDO, bucketBO);

        return bucketBO;
    }

    @Override
    public Boolean existFile(String filePath, String bucket) {
        //获取bucket信息
        Bucket bucketBO = findBucket(bucket);
        return existFile(filePath, bucketBO);
    }

    public Boolean existFile(String filePath, Bucket bucketBO) {
        //获取bucket对应的文件操作工具实例
        FileOperateService fileOperate = FileOperateFactory.getInstance(bucketBO);
        return fileOperate.existFile(bucketBO.getBucket(), filePath);
    }

    @Override
    public DynaBean findById(String id) {
        return metaService.selectOne("JE_DOCUMENT_FILE", ConditionsWrapper.builder().eq("JE_DOCUMENT_FILE_ID", id));
    }

    @Override
    public void deleteById(String id) {
        metaService.delete("JE_DOCUMENT_FILE", ConditionsWrapper.builder().eq("JE_DOCUMENT_FILE_ID", id));
    }

    @Override
    public DynaBean selectFileByDigestBean(String bucket, String digestType, String digestValue) {
        return metaService.selectOne("JE_DOCUMENT_FILE", ConditionsWrapper.builder()
                .eq("IS_DELETED", 0)
                .eq("DIGEST_TYPE", digestType)
                .eq("DIGEST_VALUE", digestValue)
                .eq("BUCKET", bucket));
    }

    @Override
    public DynaBean selectFileByDigestBucket(String digestCode, String digestValue, String bucket) {
        List<DynaBean> dynaBeanList = metaService.select("JE_DOCUMENT_FILE", ConditionsWrapper.builder()
                .eq("IS_DELETED", 0)
                .eq("DIGEST_TYPE", digestCode)
                .eq("DIGEST_VALUE", digestValue)
                .eq("BUCKET", bucket).orderByDesc("create_time"));
        DynaBean bean = null;
        if (dynaBeanList != null && dynaBeanList.size() > 0) {
            bean = dynaBeanList.get(0);
        }
        return bean;
    }

    @Override
    public DocumentFileDO selectFileByDigest(String bucket, String digestType, String digestValue) {
        DynaBean bean = selectFileByDigestBucket(bucket, digestType, digestValue);
        return BeanMappingHelper.transform(bean.getValues(), DocumentFileDO.class);
    }

    @Override
    public void deleteFilesByFileInfo(List<Map<String, String>> fileDeleteInfos) {
        if (fileDeleteInfos == null || fileDeleteInfos.isEmpty()) {
            return;
        }
        for (Map<String, String> fileDeleteInfo : fileDeleteInfos) {
            String bucket = fileDeleteInfo.get("bucket");
            if (Strings.isNullOrEmpty(bucket)) {
                continue;
            }
            //获取bucket信息
            Bucket bucketBO = findBucket(bucket);
            //获取bucket对应的文件操作工具实例
            FileOperateService fileOperate = FileOperateFactory.getInstance(bucketBO);
            if (!Strings.isNullOrEmpty(fileDeleteInfo.get("filePath"))) {
                if (fileOperate.existFile(bucket, fileDeleteInfo.get("filePath"))) {
                    fileOperate.del(bucket, fileDeleteInfo.get("filePath"));
                }
            }
            if (!Strings.isNullOrEmpty(fileDeleteInfo.get("thumbnail"))) {
                if (fileOperate.existFile(bucket, fileDeleteInfo.get("thumbnail"))) {
                    fileOperate.del(bucket, fileDeleteInfo.get("thumbnail"));
                }
            }
        }
    }
}